﻿using MongoData;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;

namespace MongoCollectionTest
{
    class Program
    {
        public static List<string> NameList = new List<string>()
        {
            "张三",
            "李四",
            "王二麻子",
            "i张三",
            "u李四",
            "o王二麻子",
            "q张三",
            "e李四",
            "t王二麻子"
        };
        static void Main(string[] args)
        {
            Console.WriteLine("-------------------欢迎使用MongoDB查询测试工具(Create By:Spring)-------------------");

            while (true)
            {
                Console.WriteLine("\r\n您可以输入以下指令:");
                Console.WriteLine("1---查询测试");
                Console.WriteLine("2---查看帮助");
                var menuKey = Console.ReadKey();
                if (menuKey.KeyChar == '1')
                {

                    while (true)
                    {
                        Console.WriteLine("\r\n集合:");
                        var collectionName = Console.ReadLine();
                        Console.WriteLine("查询:");
                        var queryStr = Console.ReadLine();
                        Console.WriteLine("排序:");
                        var soryByStr = Console.ReadLine();
                        Excute(collectionName, queryStr, soryByStr);
                        Console.WriteLine("是否继续Y/N:");
                        var isContinue = Console.ReadKey();
                        if (isContinue.KeyChar == 'N')
                        {
                            break;
                        }
                    }
                }
                else if (menuKey.KeyChar == '2')
                {
                    Console.WriteLine("-------------------常见查询结果说明-------------------\r\n");
                    Console.WriteLine("扫描类型:\r\n\r\nCOLLSCAN---全表扫描\r\n\r\nFETCH----结果中循环，常见索引扫描完毕后，从索引结果中循环筛选\r\n\r\nIXSCAN:索引扫描\r\n\r\n");
                    Console.WriteLine("扫描类型:\r\n\r\nCOLLSCAN---全表扫描\r\n\r\nFETCH----结果中循环，常见索引扫描完毕后，从索引结果中循环筛选\r\n\r\nIXSCAN:索引扫描\r\n\r\n");

                    Console.WriteLine("-------------------常见查询结果说明-------------------\r\n");

                }
            }

            Console.ReadLine();
        }
        public static void Excute(string collectionName, string queryStr, string soryByStr)
        {
            try
            {
                var sortBsonDoc = new BsonDocument();
                IMongoSortBy sortBuilder = new MySortByBuilder(sortBsonDoc);
                if (!string.IsNullOrEmpty(soryByStr))
                {
                    sortBsonDoc = MongoDB.Bson.Serialization
                         .BsonSerializer.Deserialize<BsonDocument>(soryByStr);
                    sortBuilder = new MySortByBuilder(sortBsonDoc);
                }
                var userCollection = new MogonDbFactory().Database.GetCollection(collectionName);
                var doc = MongoDB.Bson.Serialization
                        .BsonSerializer.Deserialize<BsonDocument>(queryStr);
                var query = new QueryDocument(doc);
                BsonDocument result = userCollection.Find(query).SetSortOrder(sortBuilder).Explain(true);


                var totalnReturned = result["executionStats"]["nReturned"].ToInt64();
                var totalKeysExamined = result["executionStats"]["totalKeysExamined"].ToInt64();
                var totalDocsExamined = result["executionStats"]["totalDocsExamined"].ToInt64();
                var executionTimeMillis = result["executionStats"]["executionTimeMillis"].ToInt64();
                var queryStage = result["executionStats"]["executionStages"]["stage"].ToString();
                var queryStagenReturned = result["executionStats"]["executionStages"]["nReturned"].ToString();

                var bDoc = (result["executionStats"]["executionStages"] as BsonDocument);
                List<BsonDocument> bsonDocuments = new List<BsonDocument>();
                var isOk = GetInputStage(bDoc, bsonDocuments);
                for (int i = 0; i < bsonDocuments.Count; i++)
                {
                    var stage = bsonDocuments[i]["stage"];
                    Console.WriteLine("扫描类型:" + stage);
                    var nReturned = bsonDocuments[i]["nReturned"];
                    //COLLSCAN
                    if (stage == "COLLSCAN")
                    {
                        Console.WriteLine("第" + (i + 1) + "次----全表扫描:" + bsonDocuments[i]["docsExamined"] + "----------返回:" + nReturned);
                    }
                    else if (stage == "IXSCAN")
                    {
                        var indexName = bsonDocuments[i]["indexName"];
                        var inputkeysExamined = bsonDocuments[i]["keysExamined"];
                        Console.WriteLine("第" + (i+1) + "次----扫描索引:" + inputkeysExamined + "----------返回:" + nReturned);
                    }
                    else if (stage == "FETCH")
                    {
                        Console.WriteLine("第" + (i + 1) + "次-----筛选文档:" + bsonDocuments[i]["docsExamined"] + "----------返回:" + nReturned);
                    }
                    else if (stage == "SORT_KEY_GENERATOR")
                    {
                        Console.WriteLine("第" + (i + 1) + "次----生成排序");
                    }
                    else if (stage == "SORT")
                    {
                        Console.WriteLine("第" + (i + 1) + "次-----执行排序-----排序条目:" + bsonDocuments[i]["nReturned"] + "-----占用内存:" + bsonDocuments[i]["memUsage"]+"B");
                    }
                }
                Console.WriteLine("/*******统计*******/");
                Console.WriteLine("扫描文档:" + totalDocsExamined + "/条----------返回条目:" + totalnReturned + "----------执行时间:" + executionTimeMillis);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());

            }

        }
        public static bool GetInputStage(BsonDocument bsonDocument, List<BsonDocument> bsonDocuments)
        {
            if (bsonDocument.Contains("inputStage"))
            {
                bsonDocuments.Insert(0, bsonDocument);
                var childBsonDocument = bsonDocument["inputStage"] as BsonDocument;
                return GetInputStage(childBsonDocument, bsonDocuments);
            }
            else {
                bsonDocuments.Insert(0, bsonDocument);

                return false;
            }
        }
        public static void InitData()
        {
            var userCollection = new MogonDbFactory().Database.GetCollection<User>("User");

            Random rd = new Random();
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(rd.Next(5, 15) * 1000);
                userCollection.Insert(new User()
                {
                    _id = Guid.NewGuid().ToString(),
                    Address = "address_" + i,
                    Age = rd.Next(20, 30),
                    Name = NameList[rd.Next(0, 9)],
                    CreateTime = DateTime.Now,
                    UserType = rd.Next(1, 4)
                });
            }
        }
    }
    public class MySortByBuilder : BuilderBase, IMongoSortBy
    {
        public BsonDocument Document = new BsonDocument();
        public MySortByBuilder(BsonDocument bsonDocument)
        {
            Document = bsonDocument;
        }
        public override BsonDocument ToBsonDocument()
        {
            return this.Document;
        }

        protected override void Serialize(BsonWriter bsonWriter, Type nominalType, IBsonSerializationOptions options)
        {
            BsonDocumentSerializer.Instance.Serialize(bsonWriter, nominalType, this.Document, options);
        }
    }
}
